/*
* Copyright (c) 2010 The Turing Project
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* Except as contained in this notice, the name(s) of the above
* copyright holders shall not be used in advertising or otherwise to
* promote the sale, use or other dealings in this Software without prior
* written authorization.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
 */

package     simulator;
import java.awt.Graphics;
import java.awt.Graphics2D;

/**
 * Used to define and display a temporal trace of a particular model prameter.
 * @author David
 */
public class Trace {

    private static int _nTraces = 0;
    private int _bufferPointer;
    
    private static int _graphicHeight = 20;
    private static int _lineSpacing = 25;
    private static int _xOffset = 320;
    private static int _yOffset = 390;    
       
    private Double _measurementBuffer[] = new Double[200];
    private static int _bufferSize = 200; 
    
    private Double _verticalScale;

    private int _traceID;
    private Double _maximumTraceValue;
    private Double _minimumTraceValue;

    private int _labelPosition[] = new int[2];
    private int _graphPosition[] = new int[2];
    
    private String _measurementLabel = new String();
    private String _measurementType = new String();
    private String _measurementUnits = new String();
        
    /**
     * Creates a new trace.
     */
    public Trace() {
        _traceID = _nTraces;
        _nTraces += 1;
        _bufferPointer = 0;
        _maximumTraceValue = 0.0;
        _minimumTraceValue = 0.0;
        _verticalScale = 0.1;
        _labelPosition[0] = _xOffset;
        _labelPosition[1] = _yOffset - _traceID * _lineSpacing;
        _graphPosition[0] = _xOffset + 200;
        _graphPosition[1] = _yOffset + 5 - _traceID * _lineSpacing;
        for (int i = 0; i < _bufferSize; i ++) {
            _measurementBuffer[i] = 0.0;
        }
    }

    /**
     * Defines the contents of the trace.
     * @param itemName
     * @param itemID
     * @param measurementType
     * @param measurementUnits
     */
    public void define (String itemName, int itemID, String measurementType, String measurementUnits) {
        _measurementType = measurementType;
        _measurementUnits = measurementUnits;
        _measurementLabel = String.format("%s[%d]", itemName, itemID);
    }

    /**
     * Takes a new measurement and redraws the trace.  This method should
     * be called for each of the traces at the same time so that the plots will
     * remain synchronized.
     * @param newMeasurement
     */
    public void update(Double newMeasurement) {
        _measurementBuffer[_bufferPointer] = newMeasurement;
        _bufferPointer ++;
        if (_bufferPointer > _bufferSize - 1) _bufferPointer = 0;
        rescale();
    }

    /**
     * Used to redraw the trace.
     * @param x
     * @param y
     * @param g2d
     */
    public void redraw(Graphics2D g2d) {
        g2d.drawString(String.format("%-12.10s %-12.10s %3$.1e %4$-12.10s", 
                _measurementLabel, _measurementType, _maximumTraceValue - _minimumTraceValue,
                _measurementUnits), _labelPosition[0], _labelPosition[1]);
 
        for (int i = 0; i < _bufferSize - 1; i ++) {
            plotMeasurements(i, g2d);
        }
    }

    /**
     * Plots a dot associated with a specific buffered value in the correct place
     * on the screen. Colors the dot red if it is the current read-point. Generally
     * called from the redraw() method.
     * @param valuePointer
     * @param g2D
     * @see #redraw(java.awt.Graphics2D)
     */
    private void plotMeasurements(int valuePointer, Graphics g2d) {
        int y1 = _graphPosition[1] - (int)((_measurementBuffer[valuePointer] - _minimumTraceValue)/_verticalScale);
        int x1 = _graphPosition[0] + valuePointer;
        int y2 = _graphPosition[1] - (int)((_measurementBuffer[valuePointer + 1] - _minimumTraceValue)/_verticalScale);
        int x2 = _graphPosition[0] + (valuePointer + 1);
        g2d.drawLine(x1, y1, x2, y2);

        y1 = _graphPosition[1] + (int)(_minimumTraceValue/_verticalScale);
        g2d.drawLine(x1, y1, x2, y1);

        if (valuePointer == _bufferPointer) {
            y1 = _graphPosition[1];
            y2 = _graphPosition[1] - _graphicHeight;
            g2d.drawLine(x1, y1, x1, y2);
        }
        
    }

    /**
     * Updates the maximum and minumum values for the displayed trace and
     * adjusts the vertical scale accordingly.  This method is generally
     * called from the update() method.
     * @see #update(java.lang.Double, java.awt.Graphics2D)
     */
    private void rescale () {
        for (int i = 0; i < _bufferSize; i++) {
             if (_measurementBuffer[i] > _maximumTraceValue) {
                _maximumTraceValue = _measurementBuffer[i];
                if (_maximumTraceValue < 0.0) _maximumTraceValue = 0.0;
            } else if (_measurementBuffer[i] < _minimumTraceValue) {
                _minimumTraceValue = _measurementBuffer[i];
                if (_minimumTraceValue > 0.0) _minimumTraceValue = 0.0;
            }
        }
        _verticalScale = (_maximumTraceValue - _minimumTraceValue) / (double)_graphicHeight;
    }

}
